package goodash

import "math"

// EloConfig 包含Elo评分系统的配置参数
type EloConfig struct {
	// KFactor 决定每次比赛后评分变化的幅度
	KFactor float64
	// InitialRating 新玩家的初始评分
	InitialRating float64
}

// DefaultEloConfig 返回默认的Elo配置
// 默认K因子为32，初始评分为1500
func DefaultEloConfig() *EloConfig {
	return &EloConfig{
		KFactor:       32.0,
		InitialRating: 1500.0,
	}
}

// EloSystem 提供Elo评分计算功能
type EloSystem struct {
	config *EloConfig
}

// NewEloSystem 创建一个新的Elo评分系统实例
func NewEloSystem(config *EloConfig) *EloSystem {
	if config == nil {
		config = DefaultEloConfig()
	}
	return &EloSystem{
		config: config,
	}
}

// GetInitialRating 返回初始评分
func (e *EloSystem) GetInitialRating() float64 {
	return e.config.InitialRating
}

// SetKFactor 设置K因子
func (e *EloSystem) SetKFactor(k float64) {
	e.config.KFactor = k
}

// GetKFactor 获取当前K因子
func (e *EloSystem) GetKFactor() float64 {
	return e.config.KFactor
}

// ExpectedScore 计算玩家A击败玩家B的预期得分
// 预期得分范围为0到1，1表示确定获胜，0表示确定失败
func (e *EloSystem) ExpectedScore(ratingA, ratingB float64) float64 {
	return 1.0 / (1.0 + math.Pow(10.0, (ratingB-ratingA)/400.0))
}

// CalculateNewRating 计算玩家比赛后的新评分
// result参数表示实际结果：1.0表示胜利，0.5表示平局，0.0表示失败
func (e *EloSystem) CalculateNewRating(rating, opponentRating, result float64) float64 {
	expected := e.ExpectedScore(rating, opponentRating)
	return rating + e.config.KFactor*(result-expected)
}

// UpdateRatings 同时更新两个玩家的评分
// 返回更新后的评分 [newRatingA, newRatingB]
func (e *EloSystem) UpdateRatings(ratingA, ratingB float64, result float64) [2]float64 {
	newRatingA := e.CalculateNewRating(ratingA, ratingB, result)
	// 对于玩家B，结果是相反的
	newRatingB := e.CalculateNewRating(ratingB, ratingA, 1.0-result)
	return [2]float64{newRatingA, newRatingB}
}

// GetCustomKFactor 根据玩家评分返回自定义K因子
// 通常高水平玩家使用较小的K因子
func GetCustomKFactor(rating float64) float64 {
	switch {
	case rating < 1200:
		return 32
	case rating < 2100:
		return 24
	default:
		return 16
	}
}

// DynamicEloSystem 提供动态K因子的Elo评分系统
type DynamicEloSystem struct {
	*EloSystem
}

// NewDynamicEloSystem 创建一个使用动态K因子的Elo系统
func NewDynamicEloSystem() *DynamicEloSystem {
	return &DynamicEloSystem{
		EloSystem: NewEloSystem(DefaultEloConfig()),
	}
}

// CalculateNewRating 使用动态K因子计算新评分
func (d *DynamicEloSystem) CalculateNewRating(rating, opponentRating, result float64) float64 {
	k := GetCustomKFactor(rating)
	// 临时保存当前K因子
	oldK := d.config.KFactor
	// 设置动态K因子
	d.config.KFactor = k
	// 计算新评分
	newRating := d.EloSystem.CalculateNewRating(rating, opponentRating, result)
	// 恢复原始K因子
	d.config.KFactor = oldK
	return newRating
}

// UpdateRatings 使用动态K因子同时更新两个玩家的评分
func (d *DynamicEloSystem) UpdateRatings(ratingA, ratingB float64, result float64) [2]float64 {
	newRatingA := d.CalculateNewRating(ratingA, ratingB, result)
	newRatingB := d.CalculateNewRating(ratingB, ratingA, 1.0-result)
	return [2]float64{newRatingA, newRatingB}
}